#include <std.h>
#include <error.h>
#include <type.h>
#include <dint.h>
#include <apm.h>
#include <asm.h>
#include <timer.h>
#include <ksetting.h>

static struct apm_inst apm_inst;
kdrv apm;
static unsigned apm_override=0;

int apm_installed()
{
	if(apm_inst.errc==NULL)
		return 1;
	return NULL;
}

#define u16pm ((apm_inst.flags)&1)
#define u32pm (((apm_inst.flags)&2)>>1)
#define idlespeedlo (((apm_inst.flags)&4)>>2)
#define apmdisabled (((apm_inst.flags)&8)>>3)
#define apmdisengaged (((apm_inst.flags)&16)>>4)

#define apm_call(func,dev_id,state) _bioscall(func,dev_id,state,0,0,0,APM_SWINT)

int get_apm_info()
{
	u8 cf;
	apm_call(APM_INST,0,0);
	cf=_f&1;
	if(cf)	{
		apm_inst.errc=(u8)(_eax>>8);
		return apm_inst.errc;
	}
	apm_inst.majv=(u8)(_eax>>8);
	apm_inst.minv=(u8)(_eax);
	apm_inst.flags=(u16)_ecx;
	apm_inst.errc=NULL;
	return NULL;
}

int connect_rm()
{
	u8 cf;
	if(apm_inst.errc!=NULL)
		return -1;
	apm_call(APM_CONNECTRM,0,0);
	cf=_f&1;
	if(cf)	{
		apm_inst.errc=(u8)(_eax>>8);
		return apm_inst.errc;
	}
	return NULL;
}

int disconnect()
{
	apm_call(APM_DISCONNECT,0,0);
	return NULL;
}

int cpu_idle()
{
	apm_call(CPU_IDLE,0,0);
	return NULL;
}


int cpu_busy()
{
	apm_call(CPU_BUSY,0,0);
	return NULL;
}


int set_power_state(u16 dev_id,u16 state)
{
	apm_call(APM_PWRSTATE,dev_id,state);
	return NULL;
}

int enable_apm(u16 dev_id)
{
	apm_call(APM_ED,dev_id,1);
	return NULL;
}

int disable_apm(u16 dev_id)
{
	apm_call(APM_ED,dev_id,0);
	return NULL;
}

int restore_def(u16 dev_id)
{
	apm_call(RESTORE_DEF,dev_id,0);
	return NULL;
}

int driver_ver(u16 dev_id,u16 ver)
{
	apm_call(APM_DRVVER,dev_id,ver);
	return NULL;
}

int apm_poweroff()
{
	sane(apm_inst.errc,"Apm not there..");
	cli();
	disconnect();
	connect_rm();
	driver_ver(0,0x0101);
	set_power_state(dev_idALL,stateOFF);
	mdelay(500);
		stop("Failed system shutdown!");
	return NULL;
}

apm_dev apm_device;
kdrv apm;
int init_apm()
{
	get_apm_info();
	if(apm_inst.errc==NULL)	{
		printk("\nV:%d.%d",apm_inst.majv,apm_inst.minv);
		apm_device.apm_poweroff=apm_poweroff;
	}		
	else
		device_error(&apm," No APM");
	create_setting_val("apm_override",&apm_override);
	return SUCCESS;
}

static unsigned read_device(unsigned p0,unsigned p1,unsigned p2,unsigned p3)
{
	switch(p0)	{
		case READ_VER: return apm_inst.majv*0x100+apm_inst.minv;
		case READ_INSTALL: return (unsigned)apm_installed();
		case READ_ERROR: return apm_inst.errc;
		case READ_GET_APM_INFO: return get_apm_info();
	}
	return 0; 
}

static unsigned write_device(unsigned p0,unsigned p1,unsigned p2,unsigned p3)
{
	if(apm_override)
		return 0;
	switch(p0)	{
		case WRITE_CONNECT_RM: return connect_rm();
		case WRITE_DISCONNECT: return disconnect();
		case WRITE_CPU_IDLE: return cpu_idle();
		case WRITE_CPU_BUSY: return cpu_busy();
		case WRITE_SET_POWER_STATE: return set_power_state(p1,p2);
		case WRITE_ENABLE_APM: return enable_apm(p1);
		case WRITE_DISABLE_APM: return disable_apm(p1);
		case WRITE_RESTORE_DEF: return restore_def(p1);
		case WRITE_DRIVER_VER: return driver_ver(p1,p2);
		case WRITE_APM_POWEROFF: return apm_poweroff();
	}
	return 0;
}

kdrv apm={
	.name="apm",
	.init=init_apm,
	.reset=NULL,
	.deinit=NULL, 
	.read_device=read_device,
	.write_device=write_device,
	.device_irq=DEVICE_IRQ_UNUSED,
};


__init(apm_init,apm);

